﻿using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;

namespace Central.Linq.Mvc {

	public class InfoHelper {

		internal InfoHelper() { }

		/// <summary>
		/// Возвращает FilterDefinition на основе FilterParam
		/// </summary>
		/// <typeparam name="T">Реализатор интерфейса IPrepareable</typeparam>
		/// <param name="filterParam">фильтр с формы (view)</param>
		/// <returns></returns>
		public static FilterDefinition CreateFieldDefinition<T>(FilterParam filterParam) where T : IPreparable {
			if (filterParam == null) throw new ArgumentNullException("filterParam");
			object result = null;
			string value = !string.IsNullOrEmpty(filterParam.Value) ? filterParam.Value.ToString() : null;
			Type ty = InfoHelper.GetFieldType<T>(
				filterParam.Name.Split(
					new char[] { '|' },
					StringSplitOptions.RemoveEmptyEntries)[0]);
			if (ty == typeof(bool)) {
				result = Convert.ToBoolean(value);
			}
			else if (ty == typeof(int)) {
				result = Convert.ToInt32(value);
			}
			else if (ty == typeof(decimal) || ty == typeof(float)) {
				result = Convert.ToDecimal(value);
			}
			else if (ty == typeof(DateTime)) {
				result = !String.IsNullOrEmpty(value) ? Convert.ToDateTime(value) : default(DateTime);
			}
			else if (ty == typeof(Enum)) {
				string typeName = filterParam.Name.Split(
					new char[] { '|' },
					StringSplitOptions.RemoveEmptyEntries)[1];
				Type typetoCreate = Type.GetType(typeName);
				result = Enum.Parse(typetoCreate, value);
			}
			else {
				result = value;
			}
			string filtername = filterParam.Name.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries)[0];
			FilterOperator filterOperator = filterParam.Operator;
			return new FilterDefinition(filterParam.Id, filtername, filterOperator, result);
		}

		/// <summary>
		/// Возращает тип свойства для правильного фильтра
		/// </summary>
		/// <typeparam name="T">обычно ViewModel</typeparam>
		/// <param name="fieldname">Наименование поля</param>
		/// <returns></returns>
		public static Type GetFieldType<T>(string fieldname) where T : IPreparable {
			string fieldName = fieldname.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries)[0];

			PropertyInfo[] properties = typeof(T).GetProperties();
			PropertyInfo property = properties.SingleOrDefault(x => x.Name.Equals(fieldName));
			Type result = null;
			if (property.PropertyType.IsPrimitive || property.PropertyType.Name.Equals("String") || property.PropertyType.Equals(typeof(DateTime))) {
				result = property.PropertyType;
			}
			else {
				result = property.PropertyType.BaseType;
			}
			return result;
		}

		/// <summary>
		/// Получения допустимых операций для заданного типа данных
		/// </summary>
		/// <param name="dataType">тип данных</param>
		/// <returns>коллекция операторов</returns>
		public static Collection<string> ApplicableFilterOperators(System.Type dataType) {
			Collection<string> filterOperators = new Collection<string>();
			if (dataType == typeof(System.String)) {
				filterOperators.Add("Contains");
				filterOperators.Add("DoesNotContain");
				filterOperators.Add("StartsWith");
				filterOperators.Add("DoesNotStartsWith");
				filterOperators.Add("EndsWith");
				filterOperators.Add("DoesNotEndsWith");
				filterOperators.Add("IsNull");
				filterOperators.Add("IsNotNull");
				filterOperators.Add("IsEqualTo");
				filterOperators.Add("IsNotEqualTo");
				filterOperators.Add("IsGreaterThan");
				filterOperators.Add("IsGreaterThanOrEqualTo");
				filterOperators.Add("IsLessThan");
				filterOperators.Add("IsLessThanOrEqualTo");
			}
			else if (dataType == typeof(bool) || dataType == typeof(Enum)) {
				filterOperators.Add("IsEqualTo");
				filterOperators.Add("IsNotEqualTo");
			}
			else {
				filterOperators.Add("IsNull");
				filterOperators.Add("IsNotNull");
				filterOperators.Add("IsEqualTo");
				filterOperators.Add("IsNotEqualTo");
				filterOperators.Add("IsGreaterThan");
				filterOperators.Add("IsGreaterThanOrEqualTo");
				filterOperators.Add("IsLessThan");
				filterOperators.Add("IsLessThanOrEqualTo");
			}
			return filterOperators;
		}

		/// <summary>
		/// Получения допустимых операций для свойства по заданному типу данных
		/// </summary>
		/// <param name="fieldname">название свойства</param>
		/// <returns>коллекция операторов</returns>
		/// <returns></returns>
		public static Collection<string> ApplicableFilterOperators<T>(String fieldname) where T : IPreparable {
			return InfoHelper.ApplicableFilterOperators(InfoHelper.GetFieldType<T>(fieldname));
		}

		/// <summary>
		/// Преобразование filterOperator в строку
		/// </summary>
		/// <param name="filterOperator">filterOperator</param>
		/// <returns>строка</returns>
		public static string ToString(FilterOperator filterOperator) {
			string ret = "";
			switch (filterOperator) {
				case FilterOperator.Contains:
					ret = "{0}.Contains({1})";
					break;

				case FilterOperator.DoesNotContain:
					ret = "Not {0}.Contains({1})";
					break;

				case FilterOperator.StartsWith:
					ret = "{0}.StartsWith({1})";
					break;

				case FilterOperator.DoesNotStartsWith:
					ret = "Not {0}.StartsWith({1})";
					break;

				case FilterOperator.EndsWith:
					ret = "{0}.EndsWith({1})";
					break;

				case FilterOperator.DoesNotEndsWith:
					ret = "Not {0}.EndsWith({1})";
					break;

				case FilterOperator.IsNull:
					ret = "{0}=null";
					break;

				case FilterOperator.IsNotNull:
					ret = "{0}<>null";
					break;

				case FilterOperator.IsEqualTo:
					ret = "{0}={1}";
					break;

				case FilterOperator.IsNotEqualTo:
					ret = "{0}<>{1}";
					break;

				case FilterOperator.IsGreaterThan:
					ret = "{0}>{1}";
					break;

				case FilterOperator.IsGreaterThanOrEqualTo:
					ret = "{0}>={1}";
					break;

				case FilterOperator.IsLessThan:
					ret = "{0}<{1}";
					break;

				case FilterOperator.IsLessThanOrEqualTo:
					ret = "{0}<={1}";
					break;
				default:
					break;
			}
			return ret;
		}
	}
}